package com.cwz.quartz.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cwz.api.entity.SysJob;
import com.cwz.core.constant.ScheduleConstants;
import com.cwz.core.result.R;
import com.cwz.core.utils.date.DateUtils;
import com.cwz.core.utils.string.StringUtils;
import com.cwz.core.utils.task.TaskException;
import com.cwz.quartz.mapper.SysJobMapper;
import com.cwz.quartz.model.SysJobVo;
import com.cwz.quartz.service.SysJobService;
import com.cwz.quartz.utils.CronUtils;
import com.cwz.quartz.utils.ScheduleUtils;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 定时任务调度表 服务实现类
 * </p>
 *
 * @author Wen
 * @since 2023-08-24
 */
@Service
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements SysJobService {
	@Autowired
	private Scheduler scheduler;
	@Autowired
	private SysJobMapper sysJobMapper;

	/**
	 * 项目启动时，初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理（注：不能手动修改数据库ID和任务组名，否则会导致脏数据）
	 */
	@PostConstruct
	public void init() throws SchedulerException, TaskException {
		this.scheduler.clear();
		List<SysJob> sysJobs = this.sysJobMapper.selectList(null);
		for (SysJob sysJob : sysJobs) {
			ScheduleUtils.createScheduleJob(scheduler, sysJob);
		}
	}

	@Override
	public Map<String, Object> selectJobList(SysJobVo job) {
		Page<SysJob> page = this.sysJobMapper.selectPage(
			new Page<>(job.getPageNum(), job.getPagSize()),
			new QueryWrapper<SysJob>()
			.like(StringUtils.isNotBlank(job.getJobName()), "job_name", job.getJobName())
			.eq(StringUtils.isNotBlank(job.getJobGroup()), "job_group", job.getJobGroup())
			.eq(StringUtils.isNotBlank(job.getStatus()), "status", job.getStatus())
			.like(StringUtils.isNotBlank(job.getInvokeTarget()), "invoke_target", job.getInvokeTarget())
		);
		Map<String, Object> map = new HashMap<>();
		map.put("list", page.getRecords());
		map.put("total", page.getTotal());
		return map;
	}

	@Override
	public SysJob selectJobById(Long jobId) {
		return this.sysJobMapper.selectById(jobId);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int pauseJob(SysJob job) throws SchedulerException {
		Long jobId = job.getJobId();
		String jobGroup = job.getJobGroup();
		job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
		int rows = this.sysJobMapper.updateById(job);
		if (rows > 0) {
			this.scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
		}
		return rows;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int resumeJob(SysJob job) throws SchedulerException {
		Long jobId = job.getJobId();
		String jobGroup = job.getJobGroup();
		job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
		int rows = this.sysJobMapper.updateById(job);
		if (rows > 0) {
			this.scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
		}
		return rows;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int deleteJob(SysJob job) throws SchedulerException {
		Long jobId = job.getJobId();
		String jobGroup = job.getJobGroup();
		int rows = this.sysJobMapper.deleteById(jobId);
		if (rows > 0) {
			this.scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
		}
		return rows;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void deleteJobByIds(Long[] jobIds) throws SchedulerException {
		for (Long jobId : jobIds) {
			SysJob sysJob = selectJobById(jobId);
			deleteJob(sysJob);
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int changeStatus(SysJob job) throws SchedulerException {
		int rows = 0;
		String status = job.getStatus();
		if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {
			rows = resumeJob(job);
		}
		else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {
			rows = pauseJob(job);
		}
		return rows;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean run(SysJob job) throws SchedulerException {
		boolean result = false;
		Long jobId = job.getJobId();
		String jobGroup = job.getJobGroup();
		SysJob sysJob = selectJobById(jobId);
		// 参数（当前的 jobDataMap 是设置在 Trigger 上的）
		JobDataMap jobDataMap = new JobDataMap();
		jobDataMap.put(ScheduleConstants.TASK_PROPERTIES, sysJob);
		JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
		if (this.scheduler.checkExists(jobKey)) {
			result = true;
			this.scheduler.triggerJob(jobKey, jobDataMap);
		}
		return result;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int insertJob(SysJob job) throws SchedulerException, TaskException {
		job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
		int rows = this.sysJobMapper.insert(job);
		if (rows > 0) {
			ScheduleUtils.createScheduleJob(scheduler, selectJobById(job.getJobId()));
		}
		return rows;

	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int updateJob(SysJob job) throws SchedulerException, TaskException {
		int rows = this.sysJobMapper.updateById(job);
		if (rows > 0) {
			Long jobId = job.getJobId();
			String jobGroup = job.getJobGroup();
			JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
			// 判断是否存在
			if (this.scheduler.checkExists(jobKey)) {
				// 防止创建时存在数据问题 先移除，然后在执行创建操作
				this.scheduler.deleteJob(jobKey);
			}
			ScheduleUtils.createScheduleJob(scheduler, selectJobById(jobId));
		}
		return rows;
	}

	@Override
	public boolean checkCronExpressionIsValid(String cronExpression) {
		return CronUtils.isValid(cronExpression);
	}
}
